package Filter;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

import Exception.CustomNotImplementsFilterStrategy;
import Exception.NonExistingConstructorException;
import logging.Record;

public class CustomFilter implements FilterStrategy{
	
	private Class<?> cls;
	private Object[] constructorParams;
	private Method method;
	private Constructor<?> constructor;
	
	public CustomFilter(String implementor, Object[] params) throws ClassNotFoundException, NonExistingConstructorException, NoSuchMethodException, SecurityException, CustomNotImplementsFilterStrategy{
		//load class
		
		cls = Class.forName(implementor);
		constructorParams = params;
		if (classImplements()){
			constructor = getConstructor();
			method = getMethod();
		}else{
			throw new CustomNotImplementsFilterStrategy(implementor);
		}
	}

	
	public boolean apply(Record record){
		Object object;
		try {
			object = constructor.newInstance(constructorParams);
			return (boolean) (method.invoke(object, record));
		} catch (IllegalAccessException | IllegalArgumentException
				| InvocationTargetException | InstantiationException e) {
			e.printStackTrace();
		return false;
		}
		

			


	}
	
	/*
	 * return the method associated to method name
	 */
	private Method getMethod() throws NoSuchMethodException, SecurityException{
		Method method = cls.getMethod("apply", Record.class);
		return method;
	}
	
	/*
	 * return the constructor associated to parameters list
	 */
	private Constructor<?> getConstructor() throws NonExistingConstructorException{
		for (Constructor<?> constructor : cls.getConstructors()){
			if (constructor.getParameterTypes().length == constructorParams.length)
				return constructor;
		}
		throw new NonExistingConstructorException();
	}
	
	private boolean classImplements(){
		for (Class<?> interfaceImplemented : cls.getInterfaces()){
			if (interfaceImplemented.equals(FilterStrategy.class))
				return true;
		}
		return false;

	}


}
